<script setup>
import { onMounted, ref } from "vue";
import powerSet from "./power-set";

// 接收父组件传来的数据, definProps 返回的是整个 props 对象
// 后面的数组代表要接收的属性名，通过解构赋值的方式拿到需要的 props，但会丢失响应态
const { goods } = defineProps(["goods"]);
// sku对象数据，这里创建的是 ref 包裹的代理对象，改变时需要使用 .value 不丢失响应状态
let sku = defineModel();
// 路径字典对象，为了全局使用
let pathMap = {};
// 初始化方法
const init = () => {
  // 生成有效路径字典对象
  pathMap = getpathMap(goods);
  // 初始化禁用状态
  initDisabledStatus(goods.specs, pathMap);
};
onMounted(() => init());

// 切换选中状态
const changeSelectedStatus = (item, val) => {
  // 如果是禁用状态，直接跳出
  if (val.disabled) return;
  // item：同一排的对象
  // val：当前点击项
  // 这里面的 selected 属性是在这里添加的，通过该属性控制激活的状态
  // 切换当前项的选中状态
  val.selected = !val.selected;

  // 如果当前项被选中，取消同一排其他项的选中状态
  if (val.selected) {
    item.values.forEach((value) => {
      if (value !== val) {
        value.selected = false;
      }
    });
  }

  // 点击按钮实时更新
  updateDisabledStatus(goods.specs, pathMap);

  // 获取到用户选择的规格
  const selectedValues = getSelectedValues(goods.specs);

  // 找一下当前数组里面有没有 undefined 项，找到了返回索引，找不到返回 -1
  const index = selectedValues.findIndex((item) => item === undefined);
  // 对结果进行判断
  if (index !== -1) {
    return;
  } else {
    // 获取到路径字典的 key
    const key = getSelectedValues(goods.specs).join("-");
    // 通过 key 获取到 sku 对象的 id,会返回一个数组
    const skuIds = pathMap[key];
    // 通过 skuId 作为匹配项去 goods.value.skus 中匹配到对应的 sku 对象
    sku.value = goods.skus.find((sku) => sku.id === skuIds[0]);
    // 后面就可以对这个 sku 对象做出相应的处理，如发送给后端
    console.log(sku);
  }
};

// 生成有效路径字典对象
const getpathMap = (goods) => {
  // 创建路径字典对象，这里是用 “String:Array” 的方式进行存储的
  const pathMap = {};
  // 1.根据 skus 字段生成有效的 sku 数组
  // inventory 库存字段，大于零即有库存，过滤为一个新数组
  const effectvieSkus = goods.skus.filter((sku) => sku.inventory > 0);

  // 2.根据有效的 sku 使用子集算法 [1,2] => [[1],[2],[1,2]]
  // 遍历 sku 数组，对每一个 sku 进行操作
  effectvieSkus.forEach((sku) => {
    // 2.1 把里面的所有 valueName 拿出来组成的数组，就是每个 sku 的规格名,比如“黑色-20cm-中国”
    const selectedValArr = sku.specs.map((val) => val.valueName);
    // 2.2 使用算法获取到规格数组的所有子集
    const valueArrPowerSet = powerSet(selectedValArr);

    // 3.把得到的子集数组生成最终的路径字典对象
    valueArrPowerSet.forEach((arr) => {
      // 初始化 key，使用数组的 join 方法，即把所有元素拆开并用 - 连接
      const key = arr.join("-");
      // 进行添加动作
      // 如果字典里面存在当前值，就直接往数组里面添加上 sku 的id
      // 这里的意思是，比如“a,b,c”，不管是a还是ab还是abc，都有一个共同的a，所以a这个key会有多个值
      if (pathMap[key]) {
        // 这里会有多个值，就代表着不同的 sku
        pathMap[key].push(sku.id);
      } else {
        // 如果没有，就添加上当前值，把 sku 的 id 添加进去
        pathMap[key] = [sku.id];
      }
    });
  });

  return pathMap;
};

// 初始化禁用状态,把这个商品的规格数组传进来
const initDisabledStatus = (specs, pathMap) => {
  // 对规格数组进行遍历，比如“颜色···”
  specs.forEach((item) => {
    // 对每项规格的数据进行遍历，比如“黑色···”
    item.values.forEach((val) => {
      // 判断规格的每一项有没有在库存中，
      val.disabled = !pathMap[val.name];
    });
  });
};

// 得到选中项的规格值
const getSelectedValues = (specs) => {
  // 创建一个空数组，用来存储选中的规格值
  const arr = [];
  // 遍历规格数组
  specs.forEach((item) => {
    // 这一步就是找到被选中的那一项
    // 找到被添加 selected 为 true 的项，把其 name 字段添加到数组对应位置
    const selectedVal = item.values.find((val) => val.selected);
    // 能找到就添加其 name 属性，找不到就添加 undefined
    arr.push(selectedVal ? selectedVal.name : undefined);
  });
  return arr;
};

// 切换时更新禁用状态，这里每次调用都会把整个数据进行更新处理
const updateDisabledStatus = (specs, pathMap) => {
  // 对总规格数组进行遍历
  specs.forEach((spec, index) => {
    // 通过这个方法获取到选中的数据数组
    const selectedValues = getSelectedValues(specs);
    // 遍历每个规格项，如颜色项
    spec.values.forEach((val) => {
      // 临时创建一个副本，避免污染原数组
      const tempSelectedValues = [...selectedValues];
      // 把该规格项的 name 添加到数组中，用来检查需不需要禁用
      // 如果这里遍历的是颜色数组,会生成 [‘黑色’,undefined,undefined] 或者 ["蓝色",undefined,undefined] 的数组
      tempSelectedValues[index] = val.name;
      // 对该数组进行非空值筛选，然后拼接为字符串
      const key = tempSelectedValues.filter(Boolean).join("-");
      // 如果能获取到值为真，进行取反，不禁用；获取不到值，就禁用
      val.disabled = !pathMap[key];
    });
  });
};
</script>

<template>
  <div class="goods-sku">
    <dl v-for="item in goods.specs" :key="item.id">
      <dt>{{ item.name }}</dt>
      <dd>
        <template v-for="val in item.values" :key="val.name">
          <!-- 图片类型规格 -->
          <img
            :class="{ selected: val.selected, disabled: val.disabled }"
            v-if="val.picture"
            :src="val.picture"
            :title="val.name"
            @click="changeSelectedStatus(item, val)"
          />
          <!-- 文字类型规格 -->
          <span
            :class="{ selected: val.selected, disabled: val.disabled }"
            v-else
            @click="changeSelectedStatus(item, val)"
            >{{ val.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>

<style scoped lang="scss">
@mixin sku-state-mixin {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;

  &.selected {
    border-color: #27ba9b;
  }

  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}

.goods-sku {
  padding-left: 10px;
  padding-top: 20px;

  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;

    dt {
      width: 50px;
      color: #999;
    }

    dd {
      flex: 1;
      color: #666;

      > img {
        width: 50px;
        height: 50px;
        margin-bottom: 4px;
        @include sku-state-mixin;
      }

      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        margin-bottom: 4px;
        @include sku-state-mixin;
      }
    }
  }
}
</style>
